/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.analysis;

import java.util.HashMap;
import cz.mzk.oai.metadataRepository.repository.Identifier;
import cz.mzk.oai.metadataRepository.repository.records.Record;
import cz.mzk.oai.metadataRepository.repository.records.presentAndDeleted.PresentRecord;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.XPath;

/**
 *
 * @author Martin Rehanek, Martin.Rehanek@gmail.com
 */
public class RecordsStatistics {

    private final Map<Multiset<String>, List<Identifier>> valueIdListMap = new HashMap<Multiset<String>, List<Identifier>>();
    protected int recordsProcessed = 0;
    protected int recordsDeleted = 0;
    protected int recordsFailedToLoad = 0;
    private final RecordPrintOptions recordPrint;
    private final XPath xpath;
    private final EnhanceResultOperation processResultOperation;

    public RecordsStatistics(Analysis analysis, RecordPrintOptions recordPrint) {
        this.xpath = analysis.getXpath();
        this.processResultOperation = analysis.getOperation();
        this.recordPrint = recordPrint;
    }

    public void processRecord(Record record) {
        recordsProcessed++;
        if (record.isDeleted()) {
            recordsDeleted++;
        } else {
            Document document;
            try {
                document = ((PresentRecord) record).getMetadata();
                processDocument(document, record.getId());
            } catch (IOException ex) {
                recordsFailedToLoad++;
            }
        }
    }

    private void processDocument(Document document, Identifier id) {
        List<Node> nodes = xpath.selectNodes(document);
        Multiset<String> valueMultiset = new Multiset<String>();
        for (Node node : nodes) {
            String value = node.getText();
            String valueProcessed = processResultOperation.run(value);
            valueMultiset.add(valueProcessed);
        }
        updateStatistics(valueMultiset, id);
    }

    private void updateStatistics(Multiset<String> valueMultiset, Identifier id) {
        List<Identifier> idList = valueIdListMap.get(valueMultiset);
        if (idList == null) {
            idList = new ArrayList<Identifier>();
            valueIdListMap.put(valueMultiset, idList);
        }
        idList.add(id);
        printElementOccurances(valueMultiset.size(), id);
    }

    protected void printElementOccurances(int numberOfValues, Identifier id) {
        switch (numberOfValues) {
            case 0:
                if (recordPrint.printMissing) {
                    System.out.println("no result  in " + id);
                }
                break;
            case 1:
                if (recordPrint.printPresentSingle) {
                    System.out.println("single result in " + id);
                }
                break;
            default:
                if (recordPrint.printPresentMultiple) {
                    System.out.println("present multiple results(" + numberOfValues + ") in " + id);
                }
        }
    }

    public void printResults(boolean printValues, boolean printIds) {
        System.out.println(xpath.toString());
        System.out.println("records processed: " + recordsProcessed);
        System.out.println("records failed to load: " + recordsFailedToLoad);
        MultisetSizeCounters counters = MultisetSizeCounters.byMultisetIdMap(valueIdListMap);
        System.out.println("records with single value: " + counters.single());
        System.out.println("records with multiple value: " + counters.multiple());
        System.out.println("records with missing value: " + counters.missing());
        if (printValues) {
            printMultisets(printIds);
        }
    }

    private void printMultisets(boolean printIds) {
        Map<Multiset<String>, List<Identifier>> sortedValueIdListMap = sort(valueIdListMap);
        System.out.println("multisets:" + sortedValueIdListMap.keySet().size());
        for (Multiset multiset : sortedValueIdListMap.keySet()) {
            System.out.println(multiset + ":" + sortedValueIdListMap.get(multiset).size());
            if (printIds) {
                printIdentifiers(multiset);
            }
        }
    }

    private Map<Multiset<String>, List<Identifier>> sort(Map<Multiset<String>, List<Identifier>> originalMap) {
        List<Map.Entry> entries = new ArrayList(originalMap.entrySet());
        Collections.sort(entries, new Comparator() {

            public int compare(Object first, Object second) {
                int firstInt = ((Map.Entry<Multiset<String>, List<Identifier>>) first).getValue().size();
                int secondInt = ((Map.Entry<Multiset<String>, List<Identifier>>) second).getValue().size();
                return Integer.valueOf(firstInt).compareTo(Integer.valueOf(secondInt)) * (-1);
            }
        });

        Map<Multiset<String>, List<Identifier>> result = new LinkedHashMap<Multiset<String>, List<Identifier>>();
        for (Map.Entry<Multiset<String>, List<Identifier>> entry : entries) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }

    private void printIdentifiers(Multiset multiset) {
        for (Identifier id : valueIdListMap.get(multiset)) {
            System.out.println(id);
        }
    }
}
